try:
    from functools import wraps
except ImportError:
    from django.utils.functional import wraps
import operator
from django import http, template
from django.conf import settings
from django.contrib import admin
from django.contrib.admin.sites import AlreadyRegistered, NotRegistered
from django.shortcuts import render_to_response
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext as _
from django.views.decorators.cache import never_cache
from curator.validation import clean_layout

class AlreadyDefined(Exception):
    pass

class ImproperlyConfigured(Exception):
    pass

class AdminSite(admin.AdminSite):
    """
    A subclass of Django 1.0 AdminSite.
    Mostly this can layout your registred models in a very flexible way.
    """
   
    section_index_template = None

    def __init__(self):
        super(AdminSite, self).__init__()
        self._layout = None

    def _set_layout(self, layout):
        if self._layout is not None:
            raise AlreadyDefined('A layout is already set.')
        self._layout = layout
    def _get_layout(self):
        if self._layout is None:
            return None
        if not hasattr(self, '_valid_layout'):
            self._valid_layout = clean_layout(self._layout, self._registry)
        return self._valid_layout
    layout = property(_get_layout, _set_layout)

    def layout_required(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            if self.layout is None:
                return getattr(super(AdminSite, self), f.__name__)(*args, **kwargs)
            return f(self, *args, **kwargs)
        return wrapper
    
    def get_section(self, value, key='models'):
        if self.layout:
            for section in self.layout:
                if value in section[1][key]:
                    return section
        return None

    def get_section_id(self, section):
        if self.layout:
            return self.layout.index(section) + 1
        return None

    def i18n_javascript(self, request):
        if settings.USE_I18N:
            from django.views.i18n import javascript_catalog
        else:
            from django.views.i18n import null_javascript_catalog as javascript_catalog
        return javascript_catalog(request, packages='django.conf+sorl.curator')

    @layout_required
    def index(self, request, extra_context=None):
        user = request.user
        section_list = []
        for section in self.layout:
            model_list = [] 
            module_perms = []
            for model in section[1]['models']:
                model_admin = self._registry[model]
                app_label = model._meta.app_label
                has_module_perms = user.has_module_perms(app_label)
                if has_module_perms:
                    module_perms.append(has_module_perms)
                    perms = {
                        'add': model_admin.has_add_permission(request),
                        'change': model_admin.has_change_permission(request),
                        'delete': model_admin.has_delete_permission(request),
                    }
                    # Check whether user has any perm for this module.
                    # If so, add the module to the model_list.
                    if True in perms.values():
                        model_list.append({
                            'name': capfirst(model._meta.verbose_name_plural),
                            'description': model_admin.description,
                            'admin_url': mark_safe('%s/%s/' % (app_label, model.__name__.lower())),
                            'perms': perms,
                        })
            # Have we got permissions for any models in this section?
            if model_list:
                section_list.append({
                    'name': section[0],
                    'url': "%s/" % self.get_section_id(section),
                    'has_module_perms': reduce(operator.or_, module_perms),
                    'models': model_list,
                })
        context = {
            'title': _('Site administration'),
            'section_list': section_list,
            'root_path': self.root_path,
        }
        context.update(extra_context or {})
        return render_to_response(self.index_template or 'curator/index.html', context,
            context_instance=template.RequestContext(request)
        )

    @layout_required
    def app_index(self, *args, **kwargs):
        return self.section_index(*args, **kwargs)
    
    @never_cache
    def section_index(self, request, section_id, extra_context=None):
        user = request.user
        try:
            pos = int(section_id) - 1
        except ValueError:
            raise http.Http404('No such section.')
        if 0 <= pos < len(self.layout):
            section = self.layout[pos]
        else:
            raise http.Http404('No such section.')
        model_list = []
        module_perms = []
        for model in section[1]['models']:
            app_label = model._meta.app_label
            model_admin = self._registry[model]
            has_module_perms = user.has_module_perms(app_label)
            module_perms.append(has_module_perms)
            if has_module_perms:
                perms = {
                    'add': model_admin.has_add_permission(request),
                    'change': model_admin.has_change_permission(request),
                    'delete': model_admin.has_delete_permission(request),
                }
                # Check whether user has any perm for this module.
                # If so, add the module to the model_list.
                if True in perms.values():
                    model_list.append({
                        'name': capfirst(model._meta.verbose_name_plural),
                        'description': model_admin.description,
                        'admin_url': '../%s/%s/' % (app_label, model.__name__.lower()),
                        'perms': perms,
                    })
        if not model_list:
            raise http.Http404('The requested admin page does not exist.')
       
        section_list = [{
            'name': section[0],
            'url': '',
            'has_module_perms': reduce(operator.or_, module_perms),
            'models': model_list,
        }]
        context = {
            'title': _('%s administration' % capfirst(unicode(section[0]))),
            'section_list': section_list,
            'root_path': self.root_path
        }
        context.update(extra_context or {})
        return render_to_response(self.section_index_template or 'curator/section_index.html', context,
            context_instance=template.RequestContext(request)
        )

    @layout_required
    def model_page(self, request, app_label, model_name, rest_of_url=None):
        from django.db import models
        model = models.get_model(app_label, model_name)
        if model is None:
            raise http.Http404("App %r, model %r, not found." % (app_label, model_name))
        try:
            admin_obj = self._registry[model]
        except KeyError:
            raise http.Http404("This model exists but has not been registered "
                    "with the admin site.")
        section = self.get_section(model)
        if section is None:
            raise http.Http404("This model exists but has not been defined in "
                    " the current layout.")
        return admin_obj(request, rest_of_url)

site = AdminSite()
